package com.tongjie.hotelbooking.order.service;

import com.tongjie.hotelbooking.im.conversation.entiry.Conversation;
import com.tongjie.hotelbooking.im.conversation.entiry.MemberConversation;
import com.tongjie.hotelbooking.im.conversation.repository.ConversationRepository;
import com.tongjie.hotelbooking.im.conversation.repository.MemberConversationRepository;
import com.tongjie.hotelbooking.order.entity.Order;
import com.tongjie.hotelbooking.order.entity.OrderConsulting;
import com.tongjie.hotelbooking.order.entity.RoomInfo;
import com.tongjie.hotelbooking.order.enums.OrderStatus;
import com.tongjie.hotelbooking.order.enums.SearchDirection;
import com.tongjie.hotelbooking.order.repository.OrderConsultingRepository;
import com.tongjie.hotelbooking.order.repository.OrderRepository;
import com.tongjie.hotelbooking.order.repository.RoomInfoRepository;
import com.tongjie.hotelbooking.security.SecurityHelper;
import com.tongjie.hotelbooking.user.entity.User;
import com.tongjie.hotelbooking.user.repository.UserRepository;
import org.springframework.data.domain.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.persistence.criteria.*;
import javax.transaction.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Service
public class OrderService {

    @Resource
    private OrderRepository orderRepository;

    @Resource
    private UserRepository userRepository;

    @Resource
    private OrderConsultingRepository orderConsultingRepository;

    @Resource
    private ConversationRepository conversationRepository;

    @Resource
    private MemberConversationRepository memberConversationRepository;

    @Resource
    private RoomInfoRepository roomInfoRepository;

    @Transactional
    public void createOrder(Order order) {
        User user = this.userRepository.getOne(SecurityHelper.getCurrentUserInfo().getId());
        order.setAcceptCount(0);
        order.setCreateTime(new Date());
        order.setCreateUser(user);
        order.setOrderStatus(OrderStatus.HANDLING);
        order.setPlatformCommission(order.getTotalBudget().multiply(new BigDecimal("0.1")));

        for (RoomInfo roomInfo : order.getRoomInfos()) {
            roomInfo.setOrder(order);
            this.roomInfoRepository.save(roomInfo);
        }
        this.orderRepository.save(order);
    }

    public Page<Order> findUserOrders(OrderStatus orderStatus, Long lastOrderId, SearchDirection searchDirection) {
        return this.orderRepository.findAll(((root, query, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(criteriaBuilder.equal(root.get("createUser").get("id"), SecurityHelper.getCurrentUserInfo().getId()));
            if (orderStatus != null) {
                predicateList.add(criteriaBuilder.equal(root.get("orderStatus"), orderStatus));
            }
            if (lastOrderId != null) {
                predicateList.add(SearchDirection.GT.equals(searchDirection) ? criteriaBuilder.gt(root.get("id"), lastOrderId) : criteriaBuilder.lt(root.get("id"), lastOrderId));
            }
            return criteriaBuilder.and(predicateList.toArray(new Predicate[]{}));
        }), PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "id")));
    }

    public Page<Order> searchMerchantOrders(Long lastOrderId, SearchDirection searchDirection) {
        return this.orderRepository.findAll(((root, query, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();

            predicateList.add(criteriaBuilder.equal(root.get("orderStatus"), OrderStatus.HANDLING));
            if (lastOrderId != null) {
                predicateList.add(SearchDirection.GT.equals(searchDirection) ? criteriaBuilder.gt(root.get("id"), lastOrderId) : criteriaBuilder.lt(root.get("id"), lastOrderId));
            }

            Subquery<OrderConsulting> orderConsultingSubQuery = query.subquery(OrderConsulting.class);
            Root<OrderConsulting> orderConsultingSubRoot = orderConsultingSubQuery.from(OrderConsulting.class);
            orderConsultingSubQuery.select(orderConsultingSubRoot);
            Predicate subPredicate = criteriaBuilder.and(
                    criteriaBuilder.equal(orderConsultingSubRoot.get("order"), root),
                    criteriaBuilder.equal(orderConsultingSubRoot.get("merchant").get("id"), SecurityHelper.getCurrentUserInfo().getId())
            );
            orderConsultingSubQuery.where(subPredicate);
            predicateList.add(criteriaBuilder.not(criteriaBuilder.exists(orderConsultingSubQuery)));

            return criteriaBuilder.and(predicateList.toArray(new Predicate[]{}));
        }), PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "id")));
    }

    public Order getOrderById(Long orderId) {
        return this.orderRepository.findById(orderId).orElse(null);
    }

    @Transactional
    public void acceptOrder(Long orderId) {
        Order order = this.orderRepository.getOrderByIdLock(orderId);

        User currentUser = User.builder().id(SecurityHelper.getCurrentUserInfo().getId()).build();
        User targetUser = order.getCreateUser();

        OrderConsulting orderConsulting = new OrderConsulting();
        orderConsulting.setOrder(order);
        orderConsulting.setMerchant(currentUser);
        orderConsulting.setCreateDate(new Date());
        this.orderConsultingRepository.save(orderConsulting);

        order.setAcceptCount(order.getAcceptCount() + 1);
        this.orderRepository.save(order);

        Conversation conversation = this.conversationRepository.getConversationByUsers(currentUser, targetUser);
        if (conversation == null) {
            conversation = new Conversation();
            conversation.setGuid(UUID.randomUUID().toString());
            conversation.setCreateUser(currentUser);
            conversation.setCreateTime(new Date());
            this.conversationRepository.save(conversation);

            MemberConversation ownMemberConversation = new MemberConversation();
            ownMemberConversation.setConversation(conversation);
            ownMemberConversation.setMemberUser(currentUser);
            ownMemberConversation.setMemberConversationName(targetUser.getNickName());
            ownMemberConversation.setAvatar(targetUser.getAvatar());
            this.memberConversationRepository.save(ownMemberConversation);

            MemberConversation targetMemberConversation = new MemberConversation();
            targetMemberConversation.setConversation(conversation);
            targetMemberConversation.setMemberUser(targetUser);
            targetMemberConversation.setMemberConversationName(currentUser.getNickName());
            targetMemberConversation.setAvatar(currentUser.getAvatar());
            this.memberConversationRepository.save(targetMemberConversation);
        }
    }

    public Page<Order> findMerchantOrders(Long lastId, SearchDirection searchDirection) {
        return this.orderRepository.findAll(((root, query, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            Join<Order, OrderConsulting> orderConsultingJoin = root.join("orderConsultings", JoinType.LEFT);
            predicateList.add(criteriaBuilder.equal(orderConsultingJoin.get("merchant"), User.builder().id(SecurityHelper.getCurrentUserInfo().getId()).build()));
            if (lastId != null) {
                predicateList.add(SearchDirection.GT.equals(searchDirection) ? criteriaBuilder.gt(orderConsultingJoin.get("id"), lastId) : criteriaBuilder.lt(orderConsultingJoin.get("id"), lastId));
            }
            return criteriaBuilder.and(predicateList.toArray(new Predicate[]{}));
        }), PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "id")));
    }

    public Page<Order> findOrders(Example<Order> example, Pageable pageable) {
        return this.orderRepository.findAll(example, pageable);
    }
}
